]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
status/keyboard: Limit the input method indicator to < 3 grapheme clusters
authorMike FABIAN <mfabian@redhat.com>
Tue, 17 Jun 2025 13:48:42 +0000 (15:48 +0200)
committerFlorian Müllner <fmuellner@gnome.org>
Sat, 28 Jun 2025 10:22:06 +0000 (12:22 +0200)
The Javascript string length is not suitable for measuring the
approximate length of a rendered text in the input method
indicator. Most single code point emoji like `🚀 U+1F680` get length 2
already, adding a variation selector to make the emoji display in
black and white like `🚀︎ U+1F680 U+FE0E` makes the length 3. More
complicated emoji sequences may get much longer lengths even though
the still display as one character.  For example `👩‍👩‍👧‍👧 U+1F469,
U+200D, U+1F469, U+200D, U+1F467, U+200D, U+1F467` gets length 11.
This is not only the case for emoji but also for other text, for
example the Korean jamo `각 U+1100, U+1161, U+11A8` display as only 1
glyph but the Javascript length is 3.

A better way to measure the approximate length of a rendered text is
to count the Unicode grapheme clusters in the text using
Intl.Segmenter. This counts all the examples above as 1 grapheme
cluster each.

There seem to be some bugs in the Intl.Segmenter, for example
`क्षि U+0915, U+094D, U+0937, U+093F` is counted as 2 grapheme clusters,
the correct result should be 1. But it is good enough to measure
the approximate length of the input method indicator and certainly
better than the Javascript string length.

Resolves: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/1026
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3753>
(cherry picked from commit f2246442a0ade263a1f9e775f02818bbd2862ae8)

js/ui/status/keyboard.js

index 6971ce128eec45800284191ccbb3d6ca05ce10d0..c4779d4eba65fa7f13cead7e32c2668628088ece 100644 (file)
@@ -998,6 +998,12 @@ class InputSourceIndicator extends PanelMenu.Button {
         }
     }
 
+    _getGraphemeClusters(text = '') {
+        const segmenter = new Intl.Segmenter(undefined, {granularity: 'grapheme'});
+        const segments = [...segmenter.segment(text)].map(o => o.segment);
+        return segments;
+    }
+
     _buildPropSubMenu(menu, props) {
         if (!props)
             return;
@@ -1021,7 +1027,8 @@ class InputSourceIndicator extends PanelMenu.Button {
                 let currentSource = this._inputSourceManager.currentSource;
                 if (currentSource) {
                     let indicatorLabel = this._indicatorLabels[currentSource.index];
-                    if (text && text.length > 0 && text.length < 3)
+                    const graphemeClusters = this._getGraphemeClusters(text);
+                    if (graphemeClusters.length > 0 && graphemeClusters.length < 3)
                         indicatorLabel.set_text(text);
                 }
             }